/*
 * Copyright (c) 2014 MrEngineer13
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sergivonavi.materialbanner.app.snackbar;

import com.sergivonavi.materialbanner.app.ResourceTable;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.utils.Sequenceable;

import java.io.IOException;

public class SnackBar {
    public static final short LONG_SNACK = 5000;

    public static final short MED_SNACK = 3500;

    public static final short SHORT_SNACK = 2000;

    public static final short PERMANENT_SNACK = 0;

    private SnackContainer mSnackContainer;

    // private View mParentView;
    private Component mParentView;

    private OnMessageClickListener mClickListener;

    private OnVisibilityChangeListener mVisibilityChangeListener;

    public interface OnMessageClickListener {
        void onMessageClick(Sequenceable token);
    }

    public interface OnVisibilityChangeListener {
        /**
         * Gets called when a message is shown
         *
         * @param stackSize the number of messages left to show
         */
        void onShow(int stackSize);

        /**
         * Gets called when a message is hidden
         *
         * @param stackSize the number of messages left to show
         */
        void onHide(int stackSize);
    }

    public SnackBar(Ability ability, Component component) {
        Context context = ability.getContext();
        LayoutScatter scatter = LayoutScatter.getInstance(context);
        ComponentParent componentParent = component.getComponentParent();
        if (componentParent instanceof  ComponentContainer) {
            ComponentContainer container = (ComponentContainer) componentParent;
            scatter.parse(ResourceTable.Layout_sb__snack_container, container, true);
            Component view = scatter.parse(ResourceTable.Layout_sb__snack, container, false);
            init(container, view);
        }
    }

    public SnackBar(Context context, Component container) {
        LayoutScatter scatter = LayoutScatter.getInstance(context);
        Component snackLayout = scatter.parse(ResourceTable.Layout_sb__snack, (ComponentContainer) container, false);
        if (container instanceof ComponentContainer) {
            init((ComponentContainer) container, snackLayout);
        }
    }

    private void init(ComponentContainer container, Component view) {
        Component componentById = container.findComponentById(ResourceTable.Id_snackContainer);
        if (componentById instanceof SnackContainer) {
            mSnackContainer = (SnackContainer) componentById;
        }
        if (mSnackContainer == null) {
            mSnackContainer = new SnackContainer(container);
        }
        mParentView = view;
        Component snackButton = view.findComponentById(ResourceTable.Id_snackButton);
        if (snackButton instanceof Text) {
            Text snackBtn = (Text) snackButton;
            snackBtn.setClickedListener(mButtonListener);
        }
        Component snackBar = view.findComponentById(ResourceTable.Id_snackBar);
        if (snackBar instanceof DirectionalLayout) {
            DirectionalLayout dlSnackBar = (DirectionalLayout) snackBar;
            dlSnackBar.setTouchEventListener((component, touchEvent) -> true);
        }
    }

    public static class Builder {
        private SnackBar mSnackBar;
        private Context mContext;
        private String mMessage;
        private String mActionMessage;
        private int mActionIcon = 0;
        private Sequenceable mToken;
        private short mDuration = MED_SNACK;
        private int mTextColor;
        private int mBackgroundColor;
        private int mHeight;
        private boolean mClear;
        private boolean mAnimateClear;
        private Font mTypeFace;

        /**
         * Constructs a new SnackBar
         *
         * @param ability the Ability to inflate into
         * @param component component
         */
        public Builder(Ability ability, Component component) {
            mContext = ability.getApplicationContext();
            mSnackBar = new SnackBar(ability, component);
        }

        /**
         * Constructs a new SnackBar
         *
         * @param context the context used to obtain resources
         * @param component       the view to inflate the SnackBar into
         */
        public Builder(Context context, Component component) {
            mContext = context;
            mSnackBar = new SnackBar(context, component);
        }

        /**
         * Sets the message to display on the SnackBar
         *
         * @param message the literal string to display
         * @return this builder
         */
        public Builder withMessage(String message) {
            mMessage = message;
            return this;
        }

        /**
         * Sets the message to display on the SnackBar
         *
         * @param messageId the resource id of the string to display
         * @return this builder
         * @throws NotExistException
         * @throws WrongTypeException
         * @throws IOException
         */
        public Builder withMessageId(int messageId) throws NotExistException,
                WrongTypeException, IOException {
            mMessage = mContext.getResourceManager()
                    .getSolidXml(messageId).getRoot().getStringValue();
            return this;
        }

        /**
         * Sets the message to display as the action message
         *
         * @param actionMessage the literal string to display
         * @return this builder
         */
        public Builder withActionMessage(String actionMessage) {
            mActionMessage = actionMessage;
            return this;
        }

        /**
         * Sets the message to display as the action message
         *
         * @param actionMessageResId the resource id of the string to display
         * @return this builder
         * @throws NotExistException
         * @throws WrongTypeException
         * @throws IOException
         */
        public Builder withActionMessageId(int actionMessageResId) throws NotExistException,
                WrongTypeException, IOException {
            if (actionMessageResId > 0) {
                mActionMessage = mContext.getResourceManager()
                        .getSolidXml(actionMessageResId).getRoot().getStringValue();
            }
            return this;
        }

        /**
         * Sets the action icon
         *
         * @param id the resource id of the icon to display
         * @return this builder
         */
        public Builder withActionIconId(int id) {
            mActionIcon = id;
            return this;
        }

        /**
         * Sets the {@link Style} for the action message
         *
         * @param color the {@link Style} to use
         * @return this builder
         */
        public Builder withStyle(int color) {
            mTextColor = color;
            return this;
        }

        /**
         * The token used to restore the SnackBar state
         *
         * @param token the parcelable containing the saved SnackBar
         * @return this builder
         */
        public Builder withToken(Sequenceable token) {
            mToken = token;
            return this;
        }

        /**
         * Sets the duration to show the message
         *
         * @param duration the number of milliseconds to show the message
         * @return this builder
         */
        public Builder withDuration(Short duration) {
            mDuration = duration;
            return this;
        }

        /**
         * sets the text color
         *
         * @param colorId colorId
         * @return this builder
         */
        public Builder withTextColorId(int colorId) {
            mTextColor = colorId;
            return this;
        }

        /**
         * sets the BackgroundColorId
         *
         * @param colorId the SnackBar Background color
         * @return this builder
         */
        public Builder withBackgroundColorId(int colorId) {
            mBackgroundColor = colorId;
            return this;
        }

        /**
         * Sets the height for SnackBar
         *
         * @param height the height of SnackBar
         * @return this builder
         */
        public Builder withSnackBarHeight(int height) {
            mHeight = height;
            return this;
        }

        /**
         * Sets the OnClickListener for the action button
         *
         * @param onClickListener the listener to inform of click events
         * @return this builder
         */
        public Builder withOnClickListener(OnMessageClickListener onClickListener) {
            mSnackBar.setOnClickListener(onClickListener);
            return this;
        }

        /**
         * Sets the visibilityChangeListener for the SnackBar
         *
         * @param visibilityChangeListener the listener to inform of visibility changes
         * @return this builder
         */
        public Builder withVisibilityChangeListener(OnVisibilityChangeListener visibilityChangeListener) {
            mSnackBar.setOnVisibilityChangeListener(visibilityChangeListener);
            return this;
        }

        /**
         * Clears all of the queued SnackBars, animates the message being hidden
         *
         * @return this builder
         */
        public Builder withClearQueued() {
            return withClearQueued(true);
        }

        /**
         * Clears all of the queued SnackBars
         *
         * @param animate whether or not to animate the messages being hidden
         * @return this builder
         */
        public Builder withClearQueued(boolean animate) {
            mAnimateClear = animate;
            mClear = true;
            return this;
        }

        /**
         * Sets the Typeface for the SnackBar
         *
         * @param typeFace the typeface to apply to the SnackBar
         * @return this builder
         */
        public Builder withTypeFace(Font typeFace) {
            mTypeFace = typeFace;
            return this;
        }

        /**
         * Shows the first message in the SnackBar
         *
         * @return the SnackBar
         */
        public SnackBar show() {
            Snack message = new Snack(mMessage,
                    (mActionMessage != null ? mActionMessage.toUpperCase() : null),
                    mActionIcon,
                    mToken,
                    mDuration,
                    mTextColor != 100 ? mTextColor : Color.WHITE.getValue(),
                    mBackgroundColor != 100 ? mBackgroundColor : Color.WHITE.getValue(),
                    mHeight != 0 ? mHeight : 0,
                    mTypeFace);
            if (mClear) {
                mSnackBar.clear(mAnimateClear);
            }
            mSnackBar.showMessage(message);
            return mSnackBar;
        }

        private Color getActionTextColor(Style style) {
            switch (style) {
                case ALERT:
                    return Color.RED;
                case INFO:
                    return Color.YELLOW;
                case CONFIRM:
                    return Color.GREEN;
                case DEFAULT:
                default:
                    return Color.GRAY;
            }
        }
    }

    private void showMessage(Snack message) {
        mSnackContainer.showSnack(message, mParentView, mVisibilityChangeListener);
    }


    public Component getContainerView() {
        return mParentView;
    }

    private final Component.ClickedListener mButtonListener = new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            if (mClickListener != null && mSnackContainer.isShowing()) {
                mClickListener.onMessageClick(mSnackContainer.peek().mToken);
            }
            mSnackContainer.hide();
        }
    };

    private SnackBar setOnClickListener(OnMessageClickListener listener) {
        mClickListener = listener;
        return this;
    }

    private SnackBar setOnVisibilityChangeListener(OnVisibilityChangeListener listener) {
        mVisibilityChangeListener = listener;
        return this;
    }

    /**
     * Clears all of the queued messages
     *
     * @param animate whether or not to animate the messages being hidden
     */
    public void clear(boolean animate) {
        mSnackContainer.clearSnacks(animate);
    }

    /**
     * Clears all of the queued messages
     */
    public void clear() {
        clear(true);
    }

    /**
     * Hides all snacks
     */
    public void hide() {
        mSnackContainer.hide();
        clear();
    }


    /**
     * All snacks will be restored using the view from this Snackbar
     *
     * @param state state
     */
    public void onRestoreInstanceState(Intent state) {
        mSnackContainer.restoreState(state, mParentView);
    }

    public Intent onSaveInstanceState() {
        return mSnackContainer.saveState();
    }

    public enum Style {
        DEFAULT,
        ALERT,
        CONFIRM,
        INFO
    }
}
